/*! \file
 * \brief Enter a brief description here.
 *
 * Enter a detailed description here. (optional)
 * \author Author (Ivo Wingelaar)
 * \date Date (2011)
 * \version Version (0.0.1)
 * \copyright Copyright (GNU LGPL v3)
 */

#ifndef __LYNX_VIDEO_MANAGER_HPP_INCLUDED__
#define __LYNX_VIDEO_MANAGER_HPP_INCLUDED__

#include "LynxImage.hpp"
#include "LynxLogManager.hpp"
#include "LynxMaterialRenderer.hpp"
#include "LynxGPUProgrammingServices.hpp"
#include "LynxMeshManipulator.hpp"

namespace Lynx
{

//! enumeration for geometry transformation states
enum E_TRANSFORMATION_STATE
{
    ETS_VIEW = 0,
    ETS_WORLD,
    ETS_PROJECTION,
    ETS_TEXTURE_0,
    ETS_TEXTURE_1,
    ETS_TEXTURE_2,
    ETS_TEXTURE_3,
    ETS_TEXTURE_4,
    ETS_TEXTURE_5,
    ETS_TEXTURE_6,
    ETS_TEXTURE_7,
    ETS_COUNT
};

enum E_VIDEO_MANAGER_FEATURE
{
    EVMF_RENDER_TO_TARGET = 0,
    EVMF_HARDWARE_TL,
    EVMF_MULTITEXTURE,
    EVMF_BILINEAR_FILTER,
    EVMF_MIP_MAP,
    EVMF_MIP_MAP_AUTO_UPDATE,
    EVMF_STENCIL_BUFFER,
    EVMF_VERTEX_SHADER_1_1,
    EVMF_VERTEX_SHADER_2_0,
    EVMF_VERTEX_SHADER_3_0,
    EVMF_PIXEL_SHADER_1_1,
    EVMF_PIXEL_SHADER_1_2,
    EVMF_PIXEL_SHADER_1_3,
    EVMF_PIXEL_SHADER_1_4,
    EVMF_PIXEL_SHADER_2_0,
    EVMF_PIXEL_SHADER_3_0,
    EVMF_ARB_VERTEX_PROGRAM_1,
    EVMF_ARB_FRAGMENT_PROGRAM_1,
    EVMF_ARB_GLSL,
    EVMF_TEXTURE_NSQUARE,
    EVMF_TEXTURE_NPOT,
    EVMF_FRAMEBUFFER_OBJECT,
    EVMF_VERTEX_BUFFER_OBJECT,
    EVMF_ALPHA_TO_COVERAGE,
    EVMF_COLOR_MASK,
    EVMF_MULTIPLE_RENDER_TARGETS,
    EVMF_MRT_BLEND,
    EVMF_MRT_COLOR_MASK,
    EVMF_MRT_BLEND_FUNC,
    EVMF_GEOMETRY_SHADER,
    EVMF_COUNT
};


//! Enum for the types of fog distributions to choose from
enum E_FOG_TYPE
{
    EFT_FOG_EXP=0,
    EFT_FOG_LINEAR,
    EFT_FOG_EXP2
};

//! Special render targets, which usually map to dedicated hardware
/** These render targets (besides 0 and 1) need not be supported by gfx cards */
enum E_RENDER_TARGET
{
    //! Render target is the main color frame buffer
    ERT_FRAME_BUFFER=0,
    //! Render target is a render texture
    ERT_RENDER_TEXTURE,
    //! Multi-Render target textures
    ERT_MULTI_RENDER_TEXTURES
};

class RenderTarget
{
public:
    RenderTarget(Texture* texture, E_COLOR_PLANE colorMask = ECP_ALL, E_BLEND_FACTOR blendFuncSrc = EBF_ONE, E_BLEND_FACTOR blendFuncDst = EBF_ONE_MINUS_SRC_ALPHA, bool blendEnable = false) :
        mRenderTexture(texture),
        mTargetType(ERT_RENDER_TEXTURE),
        mColorMask(colorMask),
        mBlendFuncSrc(blendFuncSrc),
        mBlendFuncDst(blendFuncDst),
        mBlendEnable(blendEnable)
    {
        //
    }

    RenderTarget(E_RENDER_TARGET target, E_COLOR_PLANE colorMask = ECP_ALL, E_BLEND_FACTOR blendFuncSrc = EBF_ONE, E_BLEND_FACTOR blendFuncDst = EBF_ONE_MINUS_SRC_ALPHA, bool blendEnable = false) :
        mRenderTexture(0),
        mTargetType(target),
        mColorMask(colorMask),
        mBlendFuncSrc(blendFuncSrc),
        mBlendFuncDst(blendFuncDst),
        mBlendEnable(blendEnable)
    {
        //
    }

    Texture* mRenderTexture;
    E_RENDER_TARGET mTargetType;
    E_COLOR_PLANE mColorMask;
    E_BLEND_FACTOR mBlendFuncSrc;
    E_BLEND_FACTOR mBlendFuncDst;
    bool mBlendEnable;
};

//! Enumeration for different types of lights
enum E_LIGHT_TYPE
{
    //! point light, it has a position in space and radiates light in all directions
    ELT_POINT,
    //! spot light, it has a position in space, a direction, and a limited cone of influence
    ELT_SPOT,
    //! directional light, coming from a direction from an infinite distance
    ELT_DIRECTIONAL
};

//! structure for holding data describing a dynamic point light.
struct Light
{
    Light() :
        mAmbientColor(0.f,0.f,0.f),
        mDiffuseColor(1.f,1.f,1.f),
        mSpecularColor(1.f,1.f,1.f),
        mAttenuation(1.f,0.f,0.f),
        mOuterCone(45.f),
        mInnerCone(0.f),
        mFalloff(2.f),
        mPosition(0.f,0.f,0.f),
        mDirection(0.f,0.f,1.f),
        mRadius(100.f),
        mType(ELT_POINT),
        mCastShadows(true)
    {
        //
    }

    //! Ambient color emitted by the light
    Colorf mAmbientColor;

    //! Diffuse color emitted by the light.
    /** This is the primary color you want to set. */
    Colorf mDiffuseColor;

    //! Specular color emitted by the light.
    /** For details how to use specular highlights, see SMaterial::Shininess */
    Colorf mSpecularColor;

    //! Attenuation factors (constant, linear, quadratic)
    /** Changes the light strength fading over distance.
    Can also be altered by setting the radius, Attenuation will change to
    (0,1.f/radius,0). Can be overridden after radius was set. */
    Myth::Math::Vector3f mAttenuation;

    //! The angle of the spot's outer cone. Ignored for other lights.
    float mOuterCone;

    //! The angle of the spot's inner cone. Ignored for other lights.
    float mInnerCone;

    //! The light strength's decrease between Outer and Inner cone.
    float mFalloff;

    //! Read-ONLY! Position of the light.
    /** If Type is ELT_DIRECTIONAL, it is ignored. Changed via light scene node's position. */
    Myth::Math::Vector3f mPosition;

    //! Read-ONLY! Direction of the light.
    /** If Type is ELT_POINT, it is ignored. Changed via light scene node's rotation. */
    Myth::Math::Vector3f mDirection;

    //! Read-ONLY! Radius of light. Everything within this radius be be lighted.
    float mRadius;

    //! Read-ONLY! Type of the light. Default: ELT_POINT
    E_LIGHT_TYPE mType;

    //! Read-ONLY! Does the light cast shadows?
    bool mCastShadows;
};

class VideoManager : public virtual Myth::ReferenceCounter
{
public:
    virtual bool begin_scene(bool backBuffer = true, bool zBuffer = true, Color color = Color(255,0,0,0), Myth::Math::Rectanglei* sourceRect = 0) = 0;

    virtual bool end_scene() = 0;

    virtual bool query_feature(E_VIDEO_MANAGER_FEATURE feature) const = 0;

    virtual void set_feature(E_VIDEO_MANAGER_FEATURE feature, bool flag = true) = 0;

    virtual void set_transform(E_TRANSFORMATION_STATE state, const Myth::Math::Matrix4 mat) = 0;

    virtual const Myth::Math::Matrix4 get_transform(E_TRANSFORMATION_STATE state) const = 0;

    virtual void set_material(const Material material) = 0;

    virtual Texture* get_texture(const std::string filename) = 0;

    virtual Texture* get_texture_by_index(uint32_t index) = 0;

    virtual uint32_t get_texture_count() const = 0;

    virtual void rename_texture(Texture* texture, const std::string newName) = 0;

    virtual Texture* add_texture(const Myth::Math::Vector2u size, const std::string name, E_COLOR_FORMAT format = ECF_A8R8G8B8) = 0;

    virtual Texture* add_texture(const std::string name, Image* image, void* mipmapData = 0) = 0;

    virtual Texture* add_render_target_texture(const Myth::Math::Vector2u size, std::string name = "rt", const E_COLOR_FORMAT format = ECF_UNKNOWN) = 0;

    virtual void remove_texture(Texture* texture) = 0;

    virtual void remove_all_textures() = 0;

    virtual void remove_hardware_buffer(const MeshBuffer* mb) = 0;

    virtual void remove_all_hardware_buffers() = 0;

    virtual bool set_render_target(Texture* texture, bool clearBackBuffer = true, bool clear_z_buffer = true, Color color = Color(0,0,0,0)) = 0;

    virtual bool set_render_target(E_RENDER_TARGET target, bool clearTarget = true, bool clear_z_buffer = true, Color color = Color(0,0,0,0)) = 0;

    virtual bool set_render_target(const std::vector<RenderTarget> texture, bool clearBackBuffer = true, bool clear_z_buffer = true, Color color = Color(0,0,0,0)) = 0;

    virtual void set_view_port(const Myth::Math::Rectanglei area) = 0;

    virtual const Myth::Math::Rectanglei get_view_port() const = 0;

    virtual void draw_vertex_primitive_list(const void* vertices, uint32_t vertexCount, const void* indexList, uint32_t primCount, E_VERTEX_TYPE vType = EVT_STANDARD, E_PRIMITIVE_TYPE pType = EPT_TRIANGLES, E_INDEX_TYPE iType = EIT_16BIT) = 0;

    virtual void draw_2d_vertex_primitive_list(const void* vertices, uint32_t vertexCount, const void* indexList, uint32_t primCount, E_VERTEX_TYPE vType = EVT_STANDARD, E_PRIMITIVE_TYPE pType = EPT_TRIANGLES, E_INDEX_TYPE iType = EIT_16BIT) = 0;

    virtual void set_clip_rectangle(Myth::Math::Rectanglei *clip = 0) = 0;

    virtual const Myth::Math::Rectanglei* get_clip_rectangle(void) const = 0;

    ///////////////////
    virtual void toggle_v_sync(bool on) = 0;
    ///////////////////

    void draw_indexed_triangle_list(const Vertex* vertices, uint32_t vertexCount, const uint16_t* indexList, uint32_t triangleCount)
    {
        draw_vertex_primitive_list(vertices, vertexCount, indexList, triangleCount, EVT_STANDARD, EPT_TRIANGLES, EIT_16BIT);
    }

    void draw_indexed_triangle_list(const Vertex2TCoords* vertices, uint32_t vertexCount, const uint16_t* indexList, uint32_t triangleCount)
    {
        draw_vertex_primitive_list(vertices, vertexCount, indexList, triangleCount, EVT_2TCOORDS, EPT_TRIANGLES, EIT_16BIT);
    }

    void draw_indexed_triangle_list(const VertexTangents* vertices, uint32_t vertexCount, const uint16_t* indexList, uint32_t triangleCount)
    {
        draw_vertex_primitive_list(vertices, vertexCount, indexList, triangleCount, EVT_TANGENTS, EPT_TRIANGLES, EIT_16BIT);
    }

    void draw_indexed_triangle_fan(const Vertex* vertices, uint32_t vertexCount, const uint16_t* indexList, uint32_t triangleCount)
    {
        draw_vertex_primitive_list(vertices, vertexCount, indexList, triangleCount, EVT_STANDARD, EPT_TRIANGLE_FAN, EIT_16BIT);
    }

    void draw_indexed_triangle_fan(const Vertex2TCoords* vertices, uint32_t vertexCount, const uint16_t* indexList, uint32_t triangleCount)
    {
        draw_vertex_primitive_list(vertices, vertexCount, indexList, triangleCount, EVT_2TCOORDS, EPT_TRIANGLE_FAN, EIT_16BIT);
    }

    void draw_indexed_triangle_fan(const VertexTangents* vertices, uint32_t vertexCount, const uint16_t* indexList, uint32_t triangleCount)
    {
        draw_vertex_primitive_list(vertices, vertexCount, indexList, triangleCount, EVT_TANGENTS, EPT_TRIANGLE_FAN, EIT_16BIT);
    }

    virtual void draw_3d_line(const Myth::Math::Vector3f start, const Myth::Math::Vector3f end, Color color = Color(255,255,255,255)) = 0;

    virtual void draw_3d_triangle(const Myth::Math::Triangle3f triangle, Color color = Color(255,255,255,255)) = 0;

    virtual void draw_3d_box(const Myth::Math::BoundingBox3f box, Color color = Color(255,255,255,255)) = 0;

    virtual void draw_2d_image(const Texture* texture, const Myth::Math::Vector2i destPos) = 0;

    virtual void draw_2d_image(const Texture* texture, const Myth::Math::Vector2i destPos, const Myth::Math::Rectanglei sourceRect, Color color=Color(255,255,255,255), bool useAlphaChannelOfTexture=false) =0;

    virtual void draw_2d_image_batch(const Texture* texture, const Myth::Math::Vector2i pos, const std::vector<Myth::Math::Rectanglei> sourceRects, const std::vector<int32_t> indices, int32_t kerningWidth = 0, Color color = Color(255,255,255,255), bool useAlphaChannelOfTexture=false) =0;

    virtual void draw_2d_image_batch(const Texture* texture, const std::vector<Myth::Math::Vector2i> positions, const std::vector<Myth::Math::Rectanglei> sourceRects, Color color=Color(255,255,255,255), bool useAlphaChannelOfTexture=false) =0;

    virtual void draw_2d_image(const Texture* texture, const Myth::Math::Rectanglei destRect, const Myth::Math::Rectanglei sourceRect, const Color* const colors=0, bool useAlphaChannelOfTexture=false) =0;

    virtual void draw_2d_rectangle(Color color, const Myth::Math::Rectanglei pos) = 0;

    virtual void draw_2d_rectangle(const Myth::Math::Rectanglei pos, Color colorLeftUp, Color colorRightUp, Color colorLeftDown, Color colorRightDown) = 0;

    virtual void draw_2d_rectangle_outline(const Myth::Math::Rectanglei pos, Color color=Color(255,255,255,255)) = 0;

    virtual void draw_2d_line(const Myth::Math::Vector2i start, const Myth::Math::Vector2i end, Color color=Color(255,255,255,255)) = 0;

    virtual void draw_2d_lines(std::vector<Myth::Math::Line2i> mLines, Color color=Color(255,255,255,255)) = 0;

    virtual void draw_pixel(uint32_t x, uint32_t y, const Color color) = 0;

    virtual void draw_2d_polygon(Myth::Math::Vector2i center, float radius, Color color=Color(100,255,255,255), int32_t vertexCount = 10) = 0;

    virtual void draw_mesh_buffer(const MeshBuffer* mb) = 0;

    virtual void set_fog(Color color = Color(0,255,255,255), E_FOG_TYPE fogType = EFT_FOG_LINEAR, float start=50.0f, float end=100.0f, float density=0.01f, bool pixelFog=false, bool rangeFog=false) = 0;

    virtual void get_fog(Color& color, E_FOG_TYPE& fogType, float& start, float& end, float& density, bool& pixelFog, bool& rangeFog) = 0;

    virtual E_COLOR_FORMAT get_color_format() const = 0;

    virtual const Myth::Math::Vector2u get_screen_size() const = 0;

    virtual const Myth::Math::Vector2u get_current_render_target_size() const = 0;

    virtual int32_t get_fps() const =0;

    virtual uint32_t get_primitive_count_drawn(uint32_t mode = 0) const = 0;

    virtual void delete_all_dynamic_lights() = 0;

    virtual int32_t add_dynamic_light(const Light light) = 0;

    virtual uint32_t get_maximal_dynamic_light_amount() const = 0;

    virtual uint32_t get_dynamic_light_count() const =0;

    virtual const Light get_dynamic_light(uint32_t idx) const =0;

    virtual void turn_light_on(int32_t lightIndex, bool turnOn) =0;

    virtual uint32_t get_maximum_primitive_count() const =0;

    virtual void set_texture_creation_flag(E_TEXTURE_CREATION_FLAG flag, bool enabled=true) = 0;

    virtual bool get_texture_creation_flag(E_TEXTURE_CREATION_FLAG flag) const = 0;

    virtual Image* create_image(Texture* texture, const Myth::Math::Vector2i pos, const Myth::Math::Vector2u size) = 0;

    virtual void on_resize(const Myth::Math::Vector2u size) = 0;

    virtual int32_t add_material_renderer(MaterialRenderer* renderer, const char* name =0) = 0;

    virtual MaterialRenderer* get_material_renderer(uint32_t idx) = 0;

    virtual uint32_t get_material_renderer_count() const = 0;

    virtual const char* get_material_renderer_name(uint32_t idx) const = 0;

    virtual void set_material_renderer_name(int32_t idx, const char* name) = 0;

    virtual GPUProgrammingServices* get_gpu_programming_services() = 0;

    virtual MeshManipulator* get_mesh_manipulator() = 0;

    virtual void clear_z_buffer() = 0;

    virtual Image* create_screen_shot() = 0;

    virtual Texture* find_texture(const std::string filename) = 0;

    virtual bool set_clip_plane(uint32_t index, const Myth::Math::Plane3f plane, bool enable = false) = 0;

    virtual void enable_clip_plane(uint32_t index, bool enable) = 0;

    virtual void set_min_hardware_buffer_vertex_count(uint32_t count) = 0;

    virtual OverrideMaterial& get_override_material() = 0;

    virtual Material& get_material_2d() = 0;

    virtual void enable_material_2d(bool enable = true) = 0;

    virtual std::string get_vender_info() = 0;

    virtual void set_ambient_light(const Colorf color) = 0;

    virtual void set_allow_z_write_on_transparent(bool flag) = 0;

    virtual Myth::Math::Vector2u get_max_texture_size() const = 0;

    virtual LogManager* get_log_manager() const = 0;
};

} // namespace Lynx

#endif // __LYNX_VIDEO_MANAGER_HPP_INCLUDED__

